/**
 * 吐司面包使用BlockingQueue的示例
 *
 * @author fireway
 * @since 2019年 02月 26日 星期二 06:30:24 CST
 */
package concurrency;

import java.util.Random;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;

class Toast {
    private final int mId;

    private Status mStatus = Status.DRY;

    public Toast(int id) {
        mId = id;
    }

    public void butter() {
        mStatus = Status.BUTTERED;
    }

    public void jam() {
        mStatus = Status.JAMMED;
    }

    public int getId() {
        return mId;
    }

    public Status getStatus() {
        return mStatus;
    }

    @Override
    public String toString() {
        return "Toast " + mId + ": " + mStatus;
    }

    public enum Status {
        DRY,
        BUTTERED,
        JAMMED
    }
}

class ToastQueue extends LinkedBlockingQueue<Toast> {
    private static final long serialVersionUID = 1L;
}

/**
 * 一个专门做吐司面包的任务
 */
class Toaster implements Runnable {
    private ToastQueue mToastQueue;

    private Random mRand = new Random(47);

    private int mCount = 0;

    public Toaster(ToastQueue tq) {
        mToastQueue = tq;
    }

    @Override
    public void run() {
        try {
            while(!Thread.interrupted()) {
                TimeUnit.MILLISECONDS.sleep(100 + mRand.nextInt(500));
                // Make toast
                Toast t = new Toast(mCount++);
                System.out.println(t);
                mToastQueue.put(t);
            }
        } catch(InterruptedException e) {
            System.out.println("Toaster interrupted");
        }
        System.out.println("Toaster off");
    }
}

/**
 * 一个专门给吐司面包抹黄油的任务
 */
class Butterer implements Runnable {
    private ToastQueue mDryQueue;

    private ToastQueue mButteredQueue;

    public Butterer(ToastQueue dryQueue, ToastQueue butteredQueue) {
        mDryQueue = dryQueue;
        mButteredQueue = butteredQueue;
    }

    @Override
    public void run() {
        try {
            while(!Thread.interrupted()) {
                // Blocks until next piece of toast is available:
                Toast t = mDryQueue.take();
                t.butter();
                System.out.println(t);
                mButteredQueue.put(t);
            }
        } catch(InterruptedException e) {
            System.out.println("Butterer interrupted");
        }
        System.out.println("Butterer off");
    }
}

/**
 * 一个专门给抹过黄油的吐司面包涂果酱的任务
 */
class Jammer implements Runnable {
    private ToastQueue mButteredQueue;

    private ToastQueue mFinishedQueue;

    public Jammer(ToastQueue butteredQueue, ToastQueue finishedQueue) {
        mButteredQueue = butteredQueue;
        mFinishedQueue = finishedQueue;
    }

    @Override
    public void run() {
        try {
            while(!Thread.interrupted()) {
                // Blocks until next piece of buttered toast is available:
                Toast t = mButteredQueue.take();
                t.jam();
                System.out.println(t);
                mFinishedQueue.put(t);
            }
        } catch(InterruptedException e) {
            System.out.println("Jammer interrupted");
        }
        System.out.println("Jammer off");
    }
}

/**
 * 食客
 */
class Eater implements Runnable {
    private ToastQueue mFinishedQueue;

    private int mCounter = 0;

    public Eater(ToastQueue finishedQueue) {
        mFinishedQueue = finishedQueue;
    }

    @Override
    public void run() {
        try {
            while(!Thread.interrupted()) {
                // Blocks until next piece of buttered toast is available:
                Toast t = mFinishedQueue.take();
                // Verify that the toast is coming in order,
                // and that all pieces are getting jammed:
                if(t.getId() != mCounter++ || t.getStatus() != Toast.Status.JAMMED) {
                    System.out.println(">>>> Error: " + t);
                    System.exit(1);
                } else {
                    System.out.println("Chomp! " + t);
                }
            }
        } catch(InterruptedException e) {
            System.out.println("Eater interrupted");
        }
        System.out.println("Eater off");
    }
}

public class ToastOMatic {
    public static void main(String[] args) {
        ExecutorService exec = Executors.newCachedThreadPool();

        ToastQueue dryQueue = new ToastQueue();
        ToastQueue butteredQueue = new ToastQueue();
        ToastQueue finishedQueue = new ToastQueue();

        exec.execute(new Toaster(dryQueue));
        exec.execute(new Butterer(dryQueue, butteredQueue));
        exec.execute(new Jammer(butteredQueue, finishedQueue));
        exec.execute(new Eater(finishedQueue));

        try {
            TimeUnit.SECONDS.sleep(5);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        exec.shutdownNow();
    }
}
